<template>
  <div class="app-container">
    <div class="left-tree" ref="treeRef" :style="{ height: `calc(100vh - ${treeTop}px - 20px)` }">
      <h3 class="f18 mb24">帮助中心内容分类</h3>
      <div class="tree-wrap">
        <el-tree
          style="width: 100%"
          ref="treeRef2"
          :data="dataSource"
          node-key="id"
          highlight-current
          draggable
          :accordion="false"
          :current-node-key="currentNodeKey"
          :default-expand-all="true"
          :expand-on-click-node="true"
          :allow-drop="allowDrop"
          @node-click="nodeClick"
          @node-drop="nodeDrop"
        >
          <!-- @node-click="nodeClick" -->
          <template #default="{ node, data }">
            <p class="custom-tree-node" @mouseover="mouseOver(node, data)" @mouseout="mouseOut(node, data)">
              <span v-if="!data.isEdit">{{ data.title }}</span>
              <el-input
                v-else
                ref="autoFocus"
                size="small"
                maxlength="10"
                v-model.trim="data.title"
                @click.stop
                @keyup.enter="editSubmit(node, data)"
                oninput="value = value.replace(/[!|@|#|$|%|^|&|*|+|~|`|,|.|！|￥]/g, '')"
              ></el-input>
              <span v-show="data.showAction || data.isEdit">
                <el-button
                  v-if="node.level === 1"
                  link
                  type="primary"
                  :icon="CirclePlusFilled"
                  @click.stop="append(node, data)"
                ></el-button>
                <el-button link type="primary" :icon="Edit" class="ml0" @click.stop="edit(node, data)"></el-button>
                <el-button
                  link
                  type="primary"
                  :icon="DeleteFilled"
                  class="ml0"
                  @click.stop="
                    () => {
                      currObj = { node, data };
                      _isOpen = true;
                    }
                  "
                ></el-button>
              </span>
            </p>
          </template>
        </el-tree>
      </div>
      <div class="add-arr-btn">
        <el-button type="primary" :icon="Plus" style="width: 100%" @click="addLevel1">新建分组</el-button>
      </div>
    </div>
    <div class="right-editor">
      <div class="title">
        <p class="f18">内容编辑</p>
        <div>
          <el-button type="primary" @click="editFun()">保存并更新</el-button>
          <!-- <el-button type="primary" plain>保存</el-button> -->
        </div>
      </div>
      <div style="border: 1px solid #ccc" v-if="html">
        <Toolbar
          style="border-bottom: 1px solid #ccc"
          :editor="editorRef"
          :defaultConfig="toolbarConfig"
          :mode="'default'"
          :key="selectedContent"
        />
        <Editor
          style="overflow-y: hidden"
          :style="{ height: `calc(100vh - ${treeTop}px - 160px - 52px)` }"
          v-model="html"
          :defaultConfig="editorConfig"
          :mode="'simple'"
          :key="selectedContent"
          @onCreated="handleCreated"
        />
      </div>
    </div>

    <!-- 删除时的二次确认框 -->
    <ActionSubmitDialog
      v-model:isOpen="_isOpen"
      :message="'温馨提示： 操作此选项后，该分组将会被永久删除，确认删除操作吗？'"
      @submit="returnSubmit"
    />
  </div>
</template>

<script setup>
import { ref, nextTick, onMounted, onBeforeUnmount, shallowRef, watch, computed } from "vue";
import { CirclePlusFilled, Edit, DeleteFilled, Plus } from "@element-plus/icons-vue";
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { getTreeList, getHelpDetail, addHelp, editHelp, delHelp, updateSort } from "@/api/mall/help";
import { ElMessage } from "element-plus";
import ActionSubmitDialog from "@/components/ActionSubmitDialog";

const autoFocus = ref();
const dataSource = ref([]);
const treeTop = ref(0);
const treeRef = ref();
const treeRef2 = ref();
const currentNode = ref({ content: "", id: "" });
const currentNodeKey = ref("");
const html = ref("");
const currObj = ref({});
const _isOpen = ref(false);

onMounted(async () => {
  setTimeout(() => {
    console.log(9090909, treeRef.value.getBoundingClientRect().top);
    treeTop.value = treeRef.value.getBoundingClientRect().top;
  }, 0);

  await getList();
  getDetail();
});

watch(
  () => currentNodeKey.value,
  val => {
    html.value = "";
    setTimeout(() => {
      html.value = currentNode.value?.content || "";
      // console.log("aaaaaaaaaaaaaaaa", currentNode.value?.content);
    }, 0);
  },
  {
    deep: true
  }
);

// 确认删除操作
const returnSubmit = () => {
  remove(currObj.value.node, currObj.value.data);
};

const getList = async () => {
  await getTreeList().then(res => {
    if (res.code === 200) {
      // console.log("tree数据===", res.data);
      dataSource.value = res.data;
      const List = JSON.parse(JSON.stringify(res.data));
      nextTick(() => {
        const firstLeafNode = findFirstLeafNode(List); //第一个叶子节点
        currentNodeKey.value = currentNodeKey.value ? currentNodeKey.value : firstLeafNode?.id;
        currentNode.value = currentNode.value?.id ? currentNode.value : firstLeafNode;
        treeRef2.value.setCurrentKey(currentNodeKey.value);
      });
      if (List.length <= 0) {
        currentNode.value = { content: "" };
        currentNodeKey.value = "";
      }
    }
  });
};

const findFirstLeafNode = tree => {
  // 定义递归函数 traverse，用于深度搜索树的每个节点
  function traverse(node) {
    // 如果当前节点为叶子节点，即没有子节点，则返回该节点
    if (!node.children || node.children.length === 0) {
      return node;
    }
    // 如果当前节点有子节点，则遍历每个子节点，继续递归调用 traverse 函数
    for (const child of node.children) {
      const result = traverse(child);
      // 如果在子节点中找到了第一个叶子节点，则立即返回该节点
      if (result) {
        return result;
      }
    }
  }

  // 外部循环遍历树的根节点
  for (const node of tree) {
    // 对于每个根节点，都调用 traverse 函数来查找第一个叶子节点
    const result = traverse(node);
    // 如果在当前根节点下找到了第一个叶子节点，则立即返回该节点
    if (result) {
      return result;
    }
  }
  return null;
};

const getDetail = () => {
  currentNodeKey.value &&
    getHelpDetail({ id: currentNodeKey.value }).then(res => {
      if (res.code === 200) {
        console.log("详情数据===", res.data);
        currentNode.value = res.data;
        // valueHtml.value = res.data.content;
      }
    });
};

const addLevel1 = () => {
  addFun({ title: "一级分组名称", parentId: 0 });
};
const append = (node, data) => {
  addFun({ title: "二级分组名称", parentId: data.id });
};

const addFun = params => {
  addHelp({ ...params, content: "初始内容", sortOrder: 1 }).then(async res => {
    if (res.code === 200) {
      await getList();
    }
  });
};

const edit = (node, data) => {
  data.isEdit = true;

  nextTick(() => {
    autoFocus.value.focus();
  });
};
const editSubmit = (node, data) => {
  if (data.title.length <= 0) return;
  data.isEdit = false;
  editFun(data);
};

const editFun = (node = null) => {
  let params = {};
  if (node) {
    console.log(111);
    params = { ...node };
  } else {
    console.log(222);
    params = { ...currentNode.value };
  }
  editHelp({ ...params, content: html.value }).then(res => {
    if (res.code === 200) {
      ElMessage({
        message: "保存更新成功",
        type: "success"
      });
      getDetail();
    }
  });
};

const remove = (node, data) => {
  delHelp({ id: data.id }).then(res => {
    if (res.code === 200) {
      getList();
      _isOpen.value = false;
    }
  });
};

const mouseOver = (node, data) => {
  data.showAction = true;
};
const mouseOut = (node, data) => {
  data.showAction = false;
};

const nodeClick = (item, node, treeNode, event) => {
  if (!node.isLeaf) return;
  console.log("点击了node节点===", item);
  currentNode.value = item;
  currentNodeKey.value = item.id;
  getDetail();
};

const allowDrop = (draggingNode, dropNode, type) => {
  // 只允许同级拖拽
  if (draggingNode.level === dropNode.level && draggingNode.data.parentId === dropNode.data.parentId) {
    if (type === "inner") {
      return false;
    } else {
      return true;
    }
  } else {
    return false;
  }
};

//拖拽成功的回调
const nodeDrop = (draggingNode, dropNode, type, event) => {
  const childArr = dropNode.level === 1 ? dropNode.parent.data : dropNode.parent.data.children;
  // console.log("拖拽结束，最后进入的节点===", childArr, dropNode);
  let ids = [];
  for (let item of childArr) {
    ids.push(item.id);
  }
  updateSort(ids).then(res => {
    if (res.code === 200) {
      ElMessage({
        message: "排序修改成功",
        type: "success"
      });
      getList();
    }
  });
};

//====================================富文本编辑器========================================================================================
// 编辑器实例，必须用 shallowRef
const editorRef = shallowRef();
// 内容 HTML
const valueHtml = ref("");
const imgUrl = ref(""); //编辑器每选择一次图片，上传后的地址
const videoUrl = ref(""); //编辑器每选择一次视频，上传后的地址

const toolbarConfig = {
  excludeKeys: ["emotion", "insertLink"] //配置不显示的菜单-上传图片、上传视频 "group-image", "group-video",
  // toolbarKeys: ["bold"] //配置想要显示的菜单
};
const editorConfig = {
  placeholder: "请输入内容...",
  MENU_CONF: {
    uploadImage: {
      customUpload: async (file, insertFn) => {
        // console.log("触发图片上传");
        await handleAvatarChangeIcon(file);
        // console.log("上传图片的地址===", imgUrl.value);
        imgUrl.value && insertFn(imgUrl.value.split("?")[0]); // 最后插入图片 insertFn(url, alt, href)，alt：描述，href:链接，后面非必填
      }
    },
    uploadVideo: {
      customUpload: async (file, insertFn) => {
        // console.log("触发视频上传");
        await handleAvatarChangeIcon(file, "video");
        console.log("上传视频的地址===", videoUrl.value);
        videoUrl.value && insertFn(videoUrl.value.split("?")[0]); // 最后插入视频 insertFn(url, alt, href)，alt：描述，href:链接，后面非必填
      }
    }
  }
};
const handleCreated = editor => {
  editorRef.value = editor; // 记录 editor 实例，重要！

  // console.log("编辑器实例===", editorRef.value.getAllMenuKeys());
  // console.log("默认配置===", editorRef.value.getConfig());
};

//为富文本编辑器添加key值，解决编辑成功后又重新打开时报错的问题
const selectedContent = computed(() => {
  return String(new Date().getTime());
});
// 组件销毁时，也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});

//====================================文件上传========================================================================================

import useMultipartOssStore from "@/store/modules/multipartOssStore.js";
const ossStore = useMultipartOssStore();
import { getFolder } from "@/utils/index";

const handleAvatarChangeIcon = async (file, type = "img") => {
  //选中文件触发的change事件
  // console.log(file);
  if (type === "video") {
    const flag = await fileType(type, [file]);

    if (!flag) {
      ElMessage({
        message: "只能上传视频文件，并且视频时长不超过45秒，大小不超过100M",
        type: "error",
        offset: 60
      });
      videoUrl.value = ""; //视频校验失败，清空视频地址
      return;
    }
  }
  await customUpload({ file }, type);
};
/**
 * 校验文件类型
 */
const fileType = async (type, fileList) => {
  let flag = true;
  if (type === "img") {
    for (let item of fileList) {
      if (!((item.type === "image/jpeg" || item.type === "image/png") && item.size <= 1024 * 1024 * 5)) {
        flag = false;
      }
      if (!(item.type === "image/jpeg" || item.type === "image/png")) {
        flag = false;
      }
    }
  } else if (type === "video") {
    for (let item of fileList) {
      console.log("视频的大小===", item);
      if (!(item.type === "video/mp4" && item.size <= 1024 * 1024 * 100)) {
        flag = false;
      }
      if (item.type === "video/mp4") {
        await getTimes(item);
        // console.log("视频的时长===", item.raw.size <= 1024 * 1024 * 100, videoTime.value);
        if (!(videoTime.value <= 45)) {
          flag = false;
        }
      }
    }
  }
  return flag;
};
/**
 * 获取视频时间长度
 */
const videoTime = ref(0);
const getTimes = async file => {
  let content = file;
  //获取录音时长
  let url = URL.createObjectURL(content);
  //经测试，发现audio也可获取视频的时长
  let audioElement = new Audio(url);
  console.log(2222);
  await new Promise((resolve, reject) => {
    audioElement.addEventListener("loadedmetadata", _event => {
      //保存获取时间长度
      videoTime.value = parseInt(audioElement.duration);
      resolve("时长获取成功");
    });
  });
};
/**
 * 上传
 */
const customUpload = async (option, type) => {
  const folder = getFolder();
  // const objName = folder + option.file.name;
  const objName = folder + option.file.name.split(".")[0] + new Date().getTime() + "." + option.file.name.split(".")[1];

  await ossStore
    .submit(objName, option.file)
    .then(async res => {
      await ossStore.setACL(objName, fileReadRole.publicRead);
      res.fileUrl = await ossStore.signatureUrl(objName);
      console.log("文件上传成功，地址为===", res.fileUrl);
      if (type === "img") {
        imgUrl.value = res.fileUrl.split("?")[0];
      } else {
        videoUrl.value = res.fileUrl.split("?")[0];
      }
    })
    .catch(err => {
      alert(JSON.stringify(err));
    });
};
const fileReadRole = {
  defaultRole: "",
  privateRole: "private",
  publicRead: "public-read",
  publicReadWrite: "public-read-write"
};
</script>

<style lang="scss" scoped>
h3,
p {
  margin: 0;
}

:deep(.w-e-textarea-video-container) {
  video {
    width: 100% !important;
  }
}
.app-container {
  display: flex;
  .left-tree {
    width: 282px;
    box-shadow: 4px 5px 16px 0px rgba(4, 19, 74, 0.04);
    border-radius: 10px 10px 10px 10px;
    padding: 20px;
    background-color: white;
    position: relative;
    .tree-wrap {
      height: calc(100% - 96px);
      overflow-y: auto;
    }

    .add-arr-btn {
      position: absolute;
      bottom: 20px;
      left: 20px;
      right: 20px;
    }
    .custom-tree-node {
      display: flex;
      justify-content: space-between;
      flex: 1;
      line-height: 32px;
      .el-button {
        color: #909399;
        font-size: 14px;
      }
    }
    :deep(.el-tree-node__content) {
      height: 32px;
      .el-input__wrapper {
        height: 32px;
        line-height: 32px;
      }
    }
  }
  .right-editor {
    flex: 1;
    margin-left: 20px;
    box-shadow: 4px 5px 16px 0px rgba(4, 19, 74, 0.04);
    border-radius: 10px 10px 10px 10px;
    padding: 20px;
    background-color: white;
    .title {
      display: flex;
      justify-content: space-between;
      margin-bottom: 20px;
    }
  }
}
</style>
